package database;


import gui.CeroRepositoryClient;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import javax.swing.JOptionPane;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;


/**
 * @author aioros
 * 
 * Manage the database xml (singleton)
 */
public class DatabaseManager {

	/*-------------------- Fields ------------------------*/
	
	
	/**
	 * database Manager
	 */
	private static DatabaseManager databaseManager = new DatabaseManager();
	
	
	/**
	 * local Database
	 */
	private Database localDatabase;
	
	
	/**
	 * default database path
	 */
	public static String DEFAULT_PATH;
	
	
	/**
	 * default path install
	 */
	public static String DEFAULT_PATH_INSTALL;
	
	
	/**
	 * default url update
	 */
	public static String DEFAULT_URL_UPDATE;
	
	/**
	 * default url install
	 */
	public static String DEFAULT_URL_INSTALL;
	
	
	/*-------------------- Constructor ------------------------*/
	
	/**
	 * Build a Database Manager
	 */
	private DatabaseManager() {
		
		try {
			
			XStream xstream = new XStream(new DomDriver());
			
			xstream.alias("paths", database.Paths.class);
			
			Paths paths = (Paths) xstream.fromXML(new FileInputStream("config.xml"));
			
			DatabaseManager.DEFAULT_PATH = paths.getDatabasePath();
			DatabaseManager.DEFAULT_PATH_INSTALL = paths.getInstallPath();
			DatabaseManager.DEFAULT_URL_UPDATE = paths.getUpdateUrl();
			DatabaseManager.DEFAULT_URL_INSTALL = paths.getInstallUrl();

			localDatabase = this.getDatabase(new FileInputStream(DatabaseManager.DEFAULT_PATH));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
		
	}

	
	
	/*-------------------- Methodes ------------------------*/
	
	
	/**
	 * read database
	 * @param xmlDatabaseInputStream input stream
	 * @return the database associate to intputStream
	 */
	public Database getDatabase(InputStream xmlDatabaseInputStream){
		
		XStream xstream = new XStream(new DomDriver());
		
		xstream.alias("database", database.Database.class);
		xstream.alias("package", database.Package.class);
		xstream.alias("branch", database.Branch.class);
		xstream.alias("release", database.Release.class);
		
		Database database = (Database) xstream.fromXML(xmlDatabaseInputStream);
		
		return database;
	}
	
	
	/**
	 * sauve database
	 * @param database has sauved
	 * @param xmlDatabaseInputStream input stream
	 */
	public void setDatabase(Database database, OutputStream xmlDatabaseOutputStream){
		
		XStream xstream = new XStream(new DomDriver());
		
		xstream.alias("database", database.Database.class);
		xstream.alias("package", database.Package.class);
		xstream.alias("branch", database.Branch.class);
		xstream.alias("release", database.Release.class);
		
		xstream.toXML(database, xmlDatabaseOutputStream);
	}
	
	
	/**
	 * sauve the local database
	 */
	public void saveLocalDatabase(){
		
		try {
			OutputStream xmlDatabaseOutputStream = new FileOutputStream(DatabaseManager.DEFAULT_PATH);
			this.setDatabase(localDatabase, xmlDatabaseOutputStream);
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(CeroRepositoryClient.getCeroRepositoryClient(),
					"File Not Found",
					"Error",
					JOptionPane.ERROR_MESSAGE);
		}
		
	}
	
	
	/**
	 * @return the database associate to path
	 */
	public Database getLocalDatabase(){
		
		return localDatabase;
	}
	
	
	/**
	 * update the database
	 * @param url update url
	 */
	public void upDateDatabase(String url){

		try {
			Database update = this.getDatabase(new URL(url).openStream());
		
			// local item
			Package lPkg;
			Branch lBch;
			Release lRls;
			
			localDatabase.setVersion(update.getVersion());
			
			for(Package p : update.getPackages().values()){
				
				this.initInstall(p);
				if (! localDatabase.getPackages().containsKey(p.getPkgid()) ){
					
					// add a package
					localDatabase.getPackages().put(p.getPkgid(), p);
				}else{
					
					// update package information
					lPkg = localDatabase.getPackages().get(p.getPkgid());
					lPkg.setName(p.getName());
					lPkg.setWebsite(p.getWebsite());
					lPkg.setRespusr(p.getRespusr());
					lPkg.setLicence(p.getLicence());
					lPkg.setCategory(p.getCategory());
					lPkg.setShortdesc(p.getShortdesc());
					lPkg.setLastupdate(p.getLastupdate());
					lPkg.setStatus(p.getStatus());
					
					for(Branch b : p.getBranches().values()){
						
						if (! lPkg.getBranches().containsKey(b.getBranchid()) ){
							
							// add a branch
							lPkg.getBranches().put(b.getBranchid(), b);
						}else{
							
							// update branch information
							lBch = lPkg.getBranches().get(b.getBranchid());
							lBch.setName(b.getName());
							lBch.setDescription(b.getDescription());
							lBch.setLastupdate(b.getLastupdate());
							lBch.setStatus(b.getStatus());
							
							for(Release r : b.getReleases().values()){
								
								if (! lBch.getReleases().containsKey(r.getRlsid()) ){
									
									// add a release
									lBch.getReleases().put(r.getRlsid(), r);
								}else {
									
									// update release information
									lRls = lBch.getReleases().get(r.getRlsid());
									lRls.setVersion(r.getVersion());
									lRls.setReleasenote(r.getReleasenote());
									lRls.setLastupdate(r.getLastupdate());
									lRls.setRlshash(r.getRlshash());
									lRls.setStatus(r.getStatus());
									if (! lRls.getInstall().equals("installed")){
										lRls.setInstall(r.getInstall());
									}
								}
							}
						}
					}
				}
			}
			
			// save the database update 
			this.saveLocalDatabase();
			
			
		}catch (MalformedURLException e) {
			JOptionPane.showMessageDialog(CeroRepositoryClient.getCeroRepositoryClient(),
					"Malformed URL : " + url,
					"Error",
					JOptionPane.ERROR_MESSAGE);
		} catch (IOException e) {
			JOptionPane.showMessageDialog(CeroRepositoryClient.getCeroRepositoryClient(),
					"IO Error",
					"Error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	
	/**
	 * file download
	 * @param urlFile url file to download
	 */
	public void fileDownload(String urlFile){
		
		URLConnection connection = null;
		InputStream is = null;
		FileOutputStream destinationFile = null;
		
		try { 
			//create the url
	        URL url = new URL(urlFile);

			//create a connection to this url
			connection = url.openConnection( );
	        
			//retrieve file length
			int length = connection.getContentLength();
			
			//if the file don't exist, a exception has throw
			if(length == -1){
				throw new IOException("Fichier vide");
	       	}

			//retrieve the file stream
			is = new BufferedInputStream(connection.getInputStream());

			//prepare the bits table in order to file data
			byte[] data = new byte[length];

			//current bit
			int currentBit = 0;
			//read position
			int deplacement = 0;
			
			//read data while deplacement < length
			while(deplacement < length){
				currentBit = is.read(data, deplacement, data.length-deplacement);	
				if(currentBit == -1)break;	
				deplacement += currentBit;
			}

			//if the end file hasn't reach, a exception has throw
			if(deplacement != length){
				throw new IOException("Le fichier n'a pas été lu en entier (seulement " 
					+ deplacement + " sur " + length + ")");
			}		
		
			//create File Output Stream to the destination file
			destinationFile = new FileOutputStream(DatabaseManager.DEFAULT_PATH_INSTALL +
					urlFile.split("/")[urlFile.split("/").length - 1]); 

			//write the file data in the stream
			destinationFile.write(data);

			//empty the cache and close the stream
			destinationFile.flush();

	      } catch (MalformedURLException e) { 
	    	  JOptionPane.showMessageDialog(CeroRepositoryClient.getCeroRepositoryClient(),
	    			  "Malformed URL : " + urlFile,
						"Error",
						JOptionPane.ERROR_MESSAGE);
	      } catch (IOException e) { 
	    	  JOptionPane.showMessageDialog(CeroRepositoryClient.getCeroRepositoryClient(),
						"IO Error",
						"Error",
						JOptionPane.ERROR_MESSAGE);
	      } finally{
	    	  try {
	    		  is.close();
				  destinationFile.close();
	    	  } catch (IOException e) {
	    		  JOptionPane.showMessageDialog(CeroRepositoryClient.getCeroRepositoryClient(),
	  					"IO Error",
	  					"Error",
	  					JOptionPane.ERROR_MESSAGE);
	    	  }
	      }
	}
	
	
	public static DatabaseManager getDatabaseManager() {
		return databaseManager;
	}
	
	
	private void initInstall(Package p){
		
		for(Branch b : p.getBranches().values()){
			
			for(Release r : b.getReleases().values()){
				
				r.setInstall("not installed");
			}
		}
	}
	
}
